package uvv001;
import robocode.*;
import java.awt.Color;
import java.util.Random;
import java.awt.geom.Point2D;

/**
 * Azail - a robot by Vasiliy Ufimtsev
 */
public class Azail extends AdvancedRobot {
	
	private EnemyBot enemy = new EnemyBot();
	
	private Random rnd = new Random();
	private int moveDir = 1;
	private boolean needScan = true;
	private long lastFire = 0;
	private long lastWallMargin = 0;
	private long lastWallHit = 0;
	private int wallMargin = 70;
	private double prevEnergy = 0;
	
	public void run() {
		setRadarColor(Color.orange);
		setScanColor(Color.orange);
		
		setAdjustGunForRobotTurn(true);
		setAdjustRadarForGunTurn(true);
		
		lastWallMargin = lastWallHit = getTime();
		
		while (true) {
			if(needScan) setTurnRadarRight(360);
			needScan = (getTime() - lastFire) > 10;
			execute();
		}
	}
    
	public void onHitWall(HitWallEvent e) {
		moveDir *= -1;
    	lastWallHit = getTime();
		setTurnLeft(getHeading() % 90);
		//waitFor(new TurnCompleteCondition(this));
	}
	
	public void onHitByBullet(HitByBulletEvent e) {
		setTurnRight(normalizeBearing(enemy.getBearing() + 90 - (45 * moveDir)));
		setAhead(150*moveDir);
		//waitFor(new MoveCompleteCondition(this));
	}
	
	
	
	public void onScannedRobot(ScannedRobotEvent e) {
		enemy.update(e, this);
		
		dodge();
		spy();
		shot();
		move();
		execute();
	}
	
	public void dodge() {
		double energyDiff = prevEnergy - enemy.getEnergy();
		if (energyDiff > 0 && energyDiff <= 3) {
			moveDir *= -1;
			setAhead((enemy.getDistance()/4+50)*moveDir);
			//waitFor(new TurnCompleteCondition(this));
		}
		prevEnergy = enemy.getEnergy();
	}
	
	public void spy() {
		double bearingToTarget = getHeading() + enemy.getBearing();
		double botToRadar = normalizeBearing(bearingToTarget - getRadarHeading());
		
		setTurnRadarRight(botToRadar);
	}
	
	public void shot() {
		double dist = enemy.getDistance();
		double power = Math.min(700/dist, 3);
		double speed = calcSpeed(power);
		double time = calcTimeToHit(speed, enemy.getDistance());
		double nextX = enemy.getNextX(time);
		double nextY = enemy.getNextY(time);
		double bearing = absoluteBearing(getX(), getY(), nextX, nextY);
		
		setTurnGunRight(normalizeBearing(bearing - getGunHeading()));
		
		if (getGunHeat() == 0 && Math.abs(getGunTurnRemaining()) < 30) {
			setFire(power);
			needScan = false;
			lastFire = getTime();
		}
	}
	
	public void move() {
		setAhead(500*moveDir);
		setTurnRight(normalizeBearing(enemy.getBearing() + 90 - (45 * moveDir)));
		
		if ((getX() <= wallMargin ||
			getX() >= getBattleFieldWidth() - wallMargin ||
			getY() <= wallMargin ||
			getY() >= getBattleFieldHeight() - wallMargin) && 
			getTime() - lastWallMargin > 500 &&
			getTime() - lastWallHit > 500
		) {
			moveDir *= -1;
			lastWallMargin = getTime();
			setAhead(1000*moveDir);
		}
		
		if (getTime() % (Math.abs(rnd.nextInt()%30)+30) == 0) {
			moveDir *= -1;
			
			setAhead(moveDir*(Math.abs(rnd.nextInt()%50) + 50));
			//waitFor(new MoveCompleteCondition(this));
		}
	}
	
	
	public double normalizeBearing(double angle) {
		while (angle > 180) angle -= 360;
		while (angle < -180) angle += 360;
		return angle;
	}
	
	public double absoluteBearing(double x1, double y1, double x2, double y2) {
		double xo = x2-x1;
		double yo = y2-y1;
		double hyp = Point2D.distance(x1, y1, x2, y2);
		double arcSin = Math.toDegrees(Math.asin(xo / hyp));
		double bearing = 0;

		if (xo > 0 && yo > 0) {
			bearing = arcSin;
		} else if (xo < 0 && yo > 0) {
			bearing = 360 + arcSin;
		} else if (xo > 0 && yo < 0) {
			bearing = 180 - arcSin;
		} else if (xo < 0 && yo < 0) {
			bearing = 180 - arcSin;
		}

		return bearing;
	}
	
	public double calcSpeed(double firePower) {
		return 20 - firePower * 3;
	}
	
	public double calcTimeToHit(double speed, double dist) {
		return dist/speed*0.9;
	}
	
}

